#pragma once

//
// The CDataQueue class implements a thread-safe queue data-structure
// with the ability of notifying the addition of data into the queue
// via a WIN32 EVENT object that is accessible through the "GetWaitHandle"
// method.  Use "Enqueue" to enqueue data and "Dequeue" to dequeue
// data (duh!).  The class assumes that the methods "Enqueue" and "Dequeue"
// can be invoked on different threads.
//
// It keeps the event signalled while there is data to be read from the
// queue.  "Dequeue" returns NULL when the queue is empty.  Being a queue
// of course it operates on a FIFO basis.
//
template<typename T>
class CDataQueue : public CAutoCriticalSectionLockable<CDataQueue<T> >
{
protected:
	HANDLE				m_hEvent;
	list<T>				m_Queue;
	bool				m_bEnabled;

public:
	CDataQueue( bool bEnabled )
	{
		m_hEvent = NULL;
		m_bEnabled = false;
		SetEnabled( bEnabled );
	}

	CDataQueue(void)
	{
		m_hEvent = NULL;
		m_bEnabled = false;
		SetEnabled( false );
	}

	~CDataQueue(void)
	{
		SetEnabled( false );
	}

	HANDLE GetWaitHandle() const
	{
		return m_hEvent;
	}

	void Clear()
	{
		//
		// clear the queue
		//
		CObjectLock lock( this );
		m_Queue.clear();

		//
		// reset the event object
		//
		if( m_hEvent != NULL )
			ResetEvent( m_hEvent );
	}

	const list<T>& GetQueue() const
	{
		return m_Queue;
	}

	void SetEnabled( bool bEnabled )
	{
		CObjectLock lock( this );

		//
		// if this is a redundant call then do nothing
		//
		if( m_bEnabled == bEnabled )
			return;

		//
		// if we are being enabled, then create the event
		// object; and if we are disabled then delete the
		// event object and reinitialize the queue
		//
		if( m_bEnabled == false )
		{
			//
			// create a manual reset event
			//
			m_hEvent = CreateEvent( NULL,
									TRUE,
									FALSE,
									NULL );
		}
		else
		{
			CloseHandle( m_hEvent );
			m_hEvent = NULL;
			m_Queue.clear();
		}

		m_bEnabled = bEnabled;
	}

	bool GetEnabled() const
	{
		return m_bEnabled;
	}

	bool Enqueue( const T& Data )
	{
		//
		// enqueue only if we are in the enabled
		// state
		//
		if( GetEnabled() == false )
			return false;

		m_cs.Lock();
		m_Queue.push_back( Data );
		m_cs.Unlock();

		//
		// notify listeners that there's data to be read
		//
		SetEvent( m_hEvent );
		return true;
	}

	bool DequeueAll( list<T>& dataList )
	{
		//
		// dequeue only if we are in the enabled
		// state
		//
		if( GetEnabled() == false )
			return false;

		CObjectLock lock( this );

		//
		// if the queue is empty then return false
		//
		if( m_Queue.size() == 0 )
			return false;

		//
		// copy all of our data into "dataList"
		//
		dataList.assign(m_Queue.begin(), m_Queue.end());
		m_Queue.clear();
		ResetEvent( m_hEvent );

		return true;
	}

	bool Dequeue( T& data )
	{
		//
		// dequeue only if we are in the enabled
		// state
		//
		if( GetEnabled() == false )
			return false;

		CObjectLock lock( this );

		//
		// if the queue is empty then return false
		//
		if( m_Queue.size() == 0 )
			return false;

		data = m_Queue.front();
		m_Queue.pop_front();

		//
		// if the queue is empty then reset the event
		//
		if( m_Queue.size() == 0 )
		{
			ResetEvent( m_hEvent );
		}

		return true;
	}
};
